---
description: >
  Various git recipes for everyday use. From moving
  commits to OS contributions via Github.
title: "Git Flows"
---

_Note_: All aliases are listed on this [gitconfig file](https://notabug.org/rem/.dots/src/master/git/.gitconfig){:rel="nofollow noreferrer noopener"}

<details markdown="1" id="table-of-contents">
<summary>
Table of Contents
</summary>

* TOC
{:toc}
</details>


## Tutorials

<div markdown="1" class="table">

| Description           | Command              |
|-----------------------|----------------------|
| Essential commands    | `git help everyday`  |
| Recommended workflows | `git help workflows` |

</div>

## Commit Messages

Since the diff already shows what changed,

- State the purpose of the changes in the heading
- Explain the context in the body


## Workflows

Assumptions:

- _centralized repos_ are hosted on some web service
- `master` is the release/production branch
- `origin` is the centralized repo we own
- `upstream` is the centralized repo we contribute to


### New features

```shell
$ git take feature-branch

# work, and commit

# update branch frequently
$ git down origin/master

# release new feature
$ git go master
$ git merge feature-branch
$ git delete feature-branch
$ git onup
```

To update `feature-branch` with `master` changes

```shell
$ git rebase master
```

To keep `feature-branch` history as part of master avoid `squash`, and
`fast-forward`.

Only rewrite history in local branches which have not been shared with
anyone.


### Patches

By design, we can contribute to any repo even when we don't have access
to the centralized repo. Most git web services allow to clone repos using
the `git://` protocol

```shell
$ git clone git://url-to-centralized/repo
```


#### Send

Once we are ready to send our changes back to the maintainer

```shell
$ git format-patch master --output-dir ./patch
```

We can also generate patches for a range of commits.

```shell
$  git format-patch --output-dir ./patch HEAD~..HEAD
```

If we worked on a new branch we might need to `rebase` before generating
a patch to prevent exceptions.

```shell
$ git down upstream/master
```

By default, git creates a patch file per commit. To create a single file
we can

```shell
$ git format-patch master --stdout > new-feature.patch
```

We can do a test run to check what we are sending

```shell
$ git send-email patch/* --subject='Fix, New feature, etc' --to='maintainer@floss-project.org' --dry-run
```

Finally, we can send our patches adding a message

```shell
$ git send-email patch/* --compose --subject='Fix, New feature, etc' --to='maintainer@floss-project.org' --quiet
```

It's considered best practice to

| best practice       | flag             |
|---------------------|------------------|
| Request comments    | `--rfc`          |
| Send multiple files | `--numbered`     |
| Detect renames      | `--find-renames` |

Only use `--find-renames` when we know the maintainers patch via `git`.

Checkout the docs for details on how to configure
[send-email](https://git-scm.com/docs/git-send-email){:rel="nofollow noreferrer noopener"},
and [format-patch](https://git-scm.com/docs/git-format-patch){:rel="nofollow noreferrer noopener"}.


#### Apply

When we receive a patch is better to review it in a feature branch.

```shell
$ git checkout review-new-feature
```

When we receive the patch as a single file we can

```shell
$ cat new-feature.patch | git am
```

For multiple files we can simply

```shell
$ cat *.patch | git am
```

We can verify we've applied the patch correctly with

```shell
$ git log --oneline
```

Beware, the SHA of the patch applied is different from the SHA of the
patch sent. Yet, the messages should be left unchanged.


### Merge requests

_aka pull request_

- Find/create an issue you can fix.
- Fork the project's repo.
- Clone fork to workstation.
- Point clone to `upstream`.

```shell
$ git remote -v     # list all remote repos
$ git remote add upstream https://github.com/ORIGINAL_OWNER/REPO.git
```

- Create a feature branch for each contribution.
- Get changes made on upstream on a regular basis.
- Push the feature branch to `origin`
- Create a merge request on the git web service.


### Search History

When debuggin focus on *how* the code got to its current state. To show
the full history of `some_code` across multiple commits and files use:

```shell
$ git log -S "some_code"
```

Use `--patch` to include the full diff, and `--reverse` to show when it
was added at the top.


### Moving Commits

#### Last `n` commits to existing branch

Uncommitted work **will** get lost.

```shell
$ git log-recent
$ git go existing-branch
$ git merge master
$ git go master
$ git undo-for-good HEAD~n
$ git go existing-branch
```

Alternatively, to move the last commit we can

```shell
git uncommit
git stash
git go correct-branch
git stash pop
git add --all # or add individually
```

#### Last `n` commits to new branch

Uncommitted work **will** get lost.

```shell
$ git log-recent
$ git branch new-branch
$ git undo-for-good a1b2c3d4
$ git go new-branch
```


### Empty commits

From time to time we need to create an empty commit to trigger the CI
pipeline

```shell
$ git commit --allow-empty --message "Trigger CI pipeline"
```

We can also use this technique to `init` an empty repo. Which allows us
to rebase the first commit.

## Change settings

### Git Multi-push

Git allows pushing to multiple remote repos. Assuming we've already set
`origin` add origin once more, as well as all mirrors:

```shell
$ git remote set-url --add --push origin git@<main-remote.org>:<username>/<repo>.git
$ git remote set-url --add --push origin git@<mirror-remote.com>:<username>/<repo>.git
```

Verify push settings:

```shell
$ git remote -v
```

Beware that we can only `pull` from `main-remote` origin repo. To get
changes from all mirrors:

```shell
$ git fetch --all
```


### Switch Git Services

Whenever we want to switch from one git service provider to another we
simply need to do:

```shell
$ cd <existing_repo>
$ git remote rename origin old-origin
$ git remote add origin git@<new-upstream.com>:<username>/<repo>.git
$ git push -u origin master
```

If we want to include all branches, and tags:

```shell
$ git push -u origin --all
$ git push -u origin --tags
```

To delete `old-origin` branches, and configuration settings:

```shell
$ git remote remove old-origin
```
